#!/usr/bin/env python

DEFAULT_TABLE_NAME = "liwe_tree_node"

class Tree ( object ):

	def __init__ ( self, liwe ):
		super ( Tree, self ).__init__ ()

		self.table_name = DEFAULT_TABLE_NAME

		self.liwe = liwe
		self.dbm = liwe.dbm


	def _sql_escape ( self, sql ):
		return sql.replace ( "'", "''" ).replace ( "\\", "\\\\" )


	def get_subtree ( self, section, id_parent = 0, join_table = None, join_field = None, join_select = None ):
		"""
		get_subtree ( section, id_parent = 0, join_table = None, join_field = None, join_select = None ) -> list

		This method gets a subtree from the database. It is also able to join the table tree with a value filled
		table specidifed with ``join_table``, using ``join_field``.

		Inputs:

			``section``	- The section name
			``id_parent``	- id of the parent folder
			``join_table``	- Table name to join results with
			``join_field``	- Field name to be used to join results with (used with ``join_table``)
			``join_select`` - Field to be added to the result list (used with ``join_table`` and ``join_field``)
		"""
		if not id_parent or id_parent == 'None':
			id_parent = 0

		conds = "n.section = '%s' AND n.id_parent = %s" % ( self._sql_escape ( section ), id_parent )
		tables = [ self.table_name + " AS n" ]
		fields = [ "n.id", "n.text", "n.folder", "n.created", "n.uid", "n.pos", "n.val" ]

		if join_table and join_field and join_select:
			conds += " AND n.val = r." + join_field
			tables.append ( join_table + " AS r" )
			fields += [ "r." + f for f in join_select ]

		rs = self.dbm.select ( tables, fields, conds, extras = "ORDER BY pos" )
		if rs: rs = rs.fetchall ()
		else: rs = []

		return rs

	def get_all_tree ( self, section, id_parent = 0 ):
		if not id_parent or id_parent == 'None':
			id_parent = 0

		conds = "n.section = '%s' AND n.id_parent = '%s'" % ( self._sql_escape ( section ), id_parent )
		tables = [ self.table_name + " AS n" ]
		fields = [ 'n.id', 'n.text', 'n.folder', 'n.val', 'n.params' ]

		rs = self.dbm.select ( tables, fields, conds, extras = "ORDER BY pos" )
		if rs: rs = rs.fetchall ()
		else: rs = []

		for rec in rs:
			if rec [ 'folder' ]:
				rec [ 'nodes' ] = self.get_all_tree ( section, rec [ 'id' ] )

		return rs


	def add_node ( self, uid, section, text, val, folder = False, params = "", id_parent = 0 ):
		"""
		add_node ( uid, section, text, val, folder = False, params = "", id_parent = 0 ) -> node id

		Adds a node to the tree
		"""

		conds = "section = '%s' AND id_parent = %s" % ( self._sql_escape ( section ), id_parent )
		r = self.dbm.single_row ( self.table_name, "MAX(pos) AS p", conds )

		if r: maxp = r [ "p" ] + 1
		else: maxp = 0

		if not folder:
			folder = 0
		else:
			folder = 1

		node = {
			"id_parent": id_parent,
			"uid": uid,
			"text": text,
			"folder": folder,
			"section": section,
			"val": val,
			"params": params,
			"created": "*NOW()",
			"pos": maxp
		}

		# The id_parent *MUST* be a folder
		self.dbm.update ( self.table_name, { "folder" : 1 }, "id = '%s'" % id_parent )

		self.dbm.insert ( self.table_name, node )
		nid = self.dbm.insert_id ()

		return nid

	def search_node_id ( self, section, val ):
		"""
		search_node_id ( section, val ) -> number

		Search for a Tree Node id given the ``section`` and ``val``.
		
		Returns the node_id or  ``0`` if not found.
		"""
		res = self.dbm.single_row ( self.table_name, "id", "section = '%s' AND val = '%s'" % ( section, val ) )
		if not res:
			return 0

		return res [ 'id' ]

	def change_attrs ( self, node_id, attrs ):
		"""
		change_attrs ( node_id, attrs )

		Bulky changes a bunch of attributes on a tree node.

		Attrs can be:
			
			text
			folder	- 0 / 1
			section
			val
			params
		"""

		change = {}
		for att in ( 'text', 'folder', 'section', 'val', 'params' ):
			if att in attrs:
				change [ att ] = attrs [ att ]

		if not len ( change ): return

		self.dbm.update ( self.table_name, change, "id = '%s'" % node_id )
		
			
	def del_node_by_val ( self, section, val ):
		conds = "section = '%s' AND val = '%s'" % ( self._sql_escape ( section ), val )
		self.dbm.delete ( self.table_name, conds )


	def del_node ( self, section, id_node ):
		"""
		del_node ( section, id_node )
		"""
		conds = "section = '%s' AND id_parent = '%s'" % ( self._sql_escape ( section ), id_node )
		rs = self.dbm.select ( self.table_name, "id", conds )
		if rs:
			for r in rs:
				self.del_node ( section, r [ "id" ] )

		conds = "section = '%s' AND id = '%s'" % ( self._sql_escape ( section ), id_node )
		self.dbm.delete ( self.table_name, conds )


	def get_node ( self, section, id_node ):
		"""
		get_node ( section, id_node )
		"""
		conds = "section = '%s' AND id = %d" % ( self._sql_escape ( section ), id_node )
		r = self.dbm.single_row ( self.table_name, 
			[ "id", "id_parent", "uid", "text", "folder", "section", "val", "params", "pos", "created" ],
			conds )
		return r


	def get_by_val ( self, section, val ):
		conds = "section = '%s' AND val = '%s'" % ( self._sql_escape ( section ), self._sql_escape ( val ) )
		r = self.dbm.single_row ( self.table_name, 
			[ "id", "id_parent", "uid", "text", "folder", "section", "val", "params", "pos", "created" ],
			conds )
		return r


	def node_down ( self, section, id_node ):
		self._switch_sibling_node ( section, id_node, ">" )

	def node_up ( self, section, id_node ):
		self._switch_sibling_node ( section, id_node, "<" )

	def _switch_sibling_node ( self, section, id_node, mode ):
		if mode == "<":
			order = "DESC"
		else:
			order = ""

		vals = self.dbm.single_row ( self.table_name, [ 'id_parent', 'pos' ], "id = '%s'" % id_node )
	
		curr_pos  = vals [ 'pos' ]
		id_parent = vals [ 'id_parent' ]

		vals = self.dbm.single_row ( self.table_name, [ 'id', 'pos' ], "id_parent = '%s' AND pos %s %s" % ( id_parent, mode, curr_pos ) , extras = "ORDER BY pos %s LIMIT 1" % order )

		if not vals: return None

		res = { "curr_pos" : curr_pos, "new_pos" : vals [ 'pos' ], "new_id" : vals [ 'id' ] }

		self.dbm.update ( self.table_name, { "pos" : res [ 'curr_pos' ] }, "id = %s" % res [ 'new_id' ] )
		self.dbm.update ( self.table_name, { "pos" : res [ 'new_pos' ] }, "id = %s" % id_node )

		
		
		

if __name__ == '__main__':
	def _test ():
		from liwe import liwe
	
		l = liwe ( cfg = 'example-liwe.cfg', home_dir = 'work' )
		
		tree = Tree ( l )

		l.dbm.delete ( 'liwe_tree_test_node' )
		l.dbm.delete ( 'liwe_tree_node' )

		folders = {}
		subs = {}

		"""
		for t in [ 'f001', 'f002', 'f003' ]:
			l.dbm.insert ( 'liwe_tree_test_node', { "myid" : t, "data" : "Folder: %s" % t } )
			folders [ t ] = tree.add_node ( 0, "test", "Folder: %s" % t, t, True )
			

		for t in [ 'abc001', 'abc002', '003', 'def004' ]:
			l.dbm.insert ( 'liwe_tree_test_node', { "myid": t, "data": "Value: %s" % t } )
			tree.add_node ( 0, "test", "Title: %s" % t, t, id_parent = folders [ 'f001' ] )

		for t in [ '2-abc001', '2-abc002', '2-003', '2-def004' ]:
			l.dbm.insert ( 'liwe_tree_test_node', { "myid": t, "data": "Value: %s" % t } )
			tree.add_node ( 0, "test", "Title: %s" % t, t, id_parent = folders [ 'f002' ] )

		for t in [ '3-abc001', '3-abc002', '3-003', '3-def004' ]:
			l.dbm.insert ( 'liwe_tree_test_node', { "myid": t, "data": "Value: %s" % t } )
			subs [ t ] = tree.add_node ( 0, "test", "Title: %s" % t, t, True, id_parent = folders [ 'f003' ] )

		for t in [ 'subs1', 'subs2', 'subs3', 'subs4' ]:
			l.dbm.insert ( 'liwe_tree_test_node', { "myid": t, "data": "Value: %s" % t } )
			tree.add_node ( 0, "test", "Title: %s" % t, t, id_parent = subs [ '3-abc001' ] )
		"""

		#print tree.get_subtree ( 'test' )

		rs = tree.get_all_tree ( 'test' )
		print rs

	def tree_to_text ( rs, level ):
		for rec in rs:
			print "\t" * level + rec [ 'text' ]
			if 'nodes' in rec:
				tree_to_text ( rec [ 'nodes' ], level + 1 )

	def _test_up ():
		from liwe import liwe

		l = liwe ( cfg = 'example-liwe.cfg', home_dir = 'work' )
		
		tree = Tree ( l )

		l.dbm.delete ( 'liwe_tree_test_node' )
		l.dbm.delete ( 'liwe_tree_node' )

		nodes = {}

		fold_id = tree.add_node ( 0, "test", "Folder", "f001", True )
		for t in ( 'node1', 'node2', 'node3' ):
			nodes [ t ] = tree.add_node ( 0, "test", t, t, id_parent = fold_id )

		l.dbm.update ( "liwe_tree_node", { "pos" : 100 }, "id = %s" % nodes [ 'node1' ] )
		l.dbm.update ( "liwe_tree_node", { "pos" : 300 }, "id = %s" % nodes [ 'node2' ] )
		l.dbm.update ( "liwe_tree_node", { "pos" : 50 }, "id = %s" % nodes [ 'node3' ] )

		tree.node_up ( "test", nodes [ 'node2' ] )
		tree.node_down ( "test", nodes [ 'node3' ] )
		
		

	_test_up ()

